﻿using Microsoft.Extensions.Caching.Distributed;
using System.Collections.Concurrent;

namespace Zhp.Cache
{
    public class MemoryCacheService : ICacheService
    {
        // 在类中添加定时清理过期项的机制
        private readonly Timer _cleanupTimer;
        private readonly IDistributedCache _cache;

        public MemoryCacheService(IDistributedCache cache)
        {
            _cache = cache;
            _cleanupTimer = new Timer(CleanupExpiredItems, null, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1)); // 后续间隔
        }

        private void CleanupExpiredItems(object state)
        {
            var now = DateTimeOffset.UtcNow;
            foreach (var kvp in _hashCache)
            {
                if (kvp.Value.Expiry.HasValue && kvp.Value.Expiry.Value < now)
                {
                    _hashCache.TryRemove(kvp.Key, out _);
                }
            }
        }

        #region 基本操作

        public T Get<T>(string key)
        {
            var bytes = _cache.Get(key);
            return bytes.BytesToObject<T>();
        }

        public async Task<T> GetAsync<T>(string key, CancellationToken token = default)
        {
            var bytes = await _cache.GetAsync(key, token);
            return bytes.BytesToObject<T>();
        }

        public void Set<T>(string key, T value, TimeSpan? expire = null)
        {
            var exp = new DistributedCacheEntryOptions() { AbsoluteExpirationRelativeToNow = expire };
            _cache.Set(key, value.ObjectToBytes(), exp);
        }

        public async Task SetAsync<T>(string key, T value, TimeSpan? expire = null, CancellationToken token = default)
        {
            var exp = new DistributedCacheEntryOptions() { AbsoluteExpirationRelativeToNow = expire };
            await _cache.SetAsync(key, value.ObjectToBytes(), exp, token);
        }

        public void Refresh(string key)
        {
            _cache.Refresh(key);
        }

        public async Task RefreshAsync(string key, CancellationToken token = default)
        {
            await _cache.RefreshAsync(key);
        }

        public void Remove(string key)
        {
            _cache.Remove(key);
        }

        public async Task RemoveAsync(string key, CancellationToken token = default)
        {
            await _cache.RemoveAsync(key, token);
        }

        public void Remove(string[] keys)
        {
            foreach (var key in keys)
            {
                _cache.Remove(key);
            }
        }

        public async Task RemoveAsync(string[] keys, CancellationToken token = default)
        {
            foreach (var key in keys)
            {
                await _cache.RemoveAsync(key, token);
            }
        }

        #endregion

        #region 集合操作

        public List<T> ListRange<T>(string key)
        {
            return Get<List<T>>(key);
        }

        public void ListRightPush<T>(string key, T value)
        {
            lock (LockHelper.Instance)
            {
                var list = Get<List<T>>(key);
                if (list == null)
                {
                    list = new List<T>();
                }
                list.Add(value);
                _cache.Set(key, list.ObjectToBytes());
            }
        }

        public void ListRightPushArray<T>(string key, T[] values)
        {
            lock (LockHelper.Instance)
            {
                var list = Get<List<T>>(key);
                if (list == null)
                {
                    list = new List<T>();
                }
                list.AddRange(values);
                _cache.Set(key, list.ObjectToBytes());
            }
        }

        public void ListRemove<T>(string key, T value)
        {
            lock (LockHelper.Instance)
            {
                var list = Get<List<T>>(key);
                if (list != null && list.Any())
                {
                    list.RemoveAll(x => x.ToJson() == value.ToJson());
                    if (list.Any())
                    {
                        _cache.Set(key, list.ObjectToBytes());
                    }
                    else
                    {
                        _cache.Remove(key);
                    }
                }
            }
        }

        public T ListLeftPop<T>(string key)
        {
            lock (LockHelper.Instance)
            {
                var list = Get<List<T>>(key);
                if (list != null && list.Any())
                {
                    var value = list.FirstOrDefault();
                    list.Remove(value);
                    if (list.Any())
                    {
                        _cache.Set(key, list.ObjectToBytes());
                    }
                    else
                    {
                        _cache.Remove(key);
                    }
                    return value;
                }
                return default;
            }
        }

        #endregion

        #region 哈希对象操作
        // 使用更高效的数据结构
        private record HashEntry(ConcurrentDictionary<string, string> Dict, DateTimeOffset? Expiry);

        private readonly ConcurrentDictionary<string, HashEntry> _hashCache = new();

        public T HashObjGet<T>(string key, string field)
        {
            if (_hashCache.TryGetValue(key, out var entry))
            {
                if (entry.Dict.TryGetValue(field, out var value))
                {
                    return value.ToJson().ToObject<T>();
                }
            }
            return default;
        }

        public T HashObjGet<T>(string key)
        {
            if (_hashCache.TryGetValue(key, out var entry))
            {
                return entry.Dict.ConDictToObject<T>();
            }
            return default;
        }

        public void HashObjSet<T>(string key, T value, TimeSpan? expire = null)
        {
            var dict = value.ObjectToConDict();

            var expiry = expire.HasValue ? DateTimeOffset.UtcNow.Add(expire.Value) : (DateTimeOffset?)null;

            _hashCache[key] = new HashEntry(dict, expiry);
        }

        public void HashObjSet(string key, string hashField, object value, TimeSpan? expire = null)
        {
            var entry = _hashCache.GetOrAdd(key, _ => new HashEntry(new ConcurrentDictionary<string, string>(), expire.HasValue ? DateTimeOffset.UtcNow.Add(expire.Value) : null));

            entry.Dict[hashField] = value.ToJson();

            // 如果需要更新过期时间
            if (expire.HasValue)
            {
                entry = entry with { Expiry = DateTimeOffset.UtcNow.Add(expire.Value) };
                _hashCache[key] = entry;
            }
        }

        public void HashObjDelete(string key)
        {
            _hashCache.TryRemove(key, out _);
        }

        #endregion

        #region 哈希集合操作优化

        public T HashListGet<T>(string key, object fieldId)
        {
            if (_hashCache.TryGetValue(key, out var entry) && entry.Dict.TryGetValue(fieldId.ToString(), out var value))
            {
                return value.ToObject<T>();
            }
            return default;
        }

        public bool HashExists(string key, object fieldId)
        {
            return _hashCache.TryGetValue(key, out var entry) && entry.Dict.ContainsKey(fieldId.ToString());
        }

        public List<T> HashListGetAll<T>(string key)
        {
            if (_hashCache.TryGetValue(key, out var entry))
            {
                return entry.Dict.Values.Select(v => v.ToObject<T>()).ToList();
            }
            return new List<T>();
        }

        public void HashListSet<T>(string key, Func<T, object> fieldFunc, T value, TimeSpan? expire = null)
        {
            var entry = _hashCache.GetOrAdd(key, _ => new HashEntry(new ConcurrentDictionary<string, string>(), expire.HasValue ? DateTimeOffset.UtcNow.Add(expire.Value) : null));

            entry.Dict[fieldFunc(value).ToString()] = value.ToJson();

            if (expire.HasValue)
            {
                _hashCache[key] = entry with { Expiry = DateTimeOffset.UtcNow.Add(expire.Value) };
            }
        }

        public void HashListSet<T>(string key, Func<T, object> fieldFunc, List<T> values, TimeSpan? expire = null)
        {
            var entry = _hashCache.GetOrAdd(key, _ => new HashEntry(new ConcurrentDictionary<string, string>(), expire.HasValue ? DateTimeOffset.UtcNow.Add(expire.Value) : null));

            foreach (var value in values)
            {
                entry.Dict[fieldFunc(value).ToString()] = value.ToJson();
            }

            if (expire.HasValue)
            {
                _hashCache[key] = entry with { Expiry = DateTimeOffset.UtcNow.Add(expire.Value) };
            }
        }

        public void HashListDelete(string key, string field)
        {
            if (_hashCache.TryGetValue(key, out var entry))
            {
                entry.Dict.TryRemove(field, out _);
            }
        }

        #endregion
    }
}
